﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using Bling.Util;
using Bling.DSL;
using Bling.Core;
using Bling.Vecs;
using Microsoft.Linq.Expressions;
using Bling.Angles;
using Bling.Objects;

namespace Bling.Play {



  /*
  public interface RenderGeometry<K, J> : Geometry<K,J>
    where K : Dim1D<K>
    where J : Dim1D<J> {

    Double4Bl VertexPosition { get; }
    ColorBl PixelColor { get; }
  }*/
}

namespace Bling.TestP {


  /*
   1--2
   | /|
   |/ |
   0--3
   */
  public class Face : PrimitiveGeometry<D3> {
    public override IntBl Count { get {  return 4; } }
    public override IntBl PrimitiveCount { get { return 2; } }
    public override FlexNumFuncBl<IntBl, IntBl<D3>> Indices {
      get {
        return new Func<IntBl,IntBl<D3>>(i => new IntBl<D3>(0, 1 + i, 2 + i)); 
      }
    }
  }

  public abstract class PrimitiveGeometry<D> where D : Dim1D<D> {
    // D1 is point, D2 is line, D3 is triangle.
    public abstract IntBl Count { get; }
    public abstract IntBl PrimitiveCount { get; }
    public abstract FlexNumFuncBl<IntBl, IntBl<D>> Indices { get; }

    private static readonly FlexNumFuncBl<IntBl, IntBl<D>> DefaultIndices = new FlexNumFuncBl<IntBl, IntBl<D>>((i) => {
      var Indices0 = new IntBl[Dim<D>.Count];
      for (int j = 0; j < Indices0.Length; j++)
        Indices0[j] = i * Dim<D>.Count + j;
      return new IntBl<D>(Indices0);
    });
    public PrimitiveGeometry() { }
    public override int GetHashCode() {
      return Count.GetHashCode() + PrimitiveCount.GetHashCode() + Indices.GetHashCode();
    }
    public override bool Equals(object obj) {
      if (object.Equals(this, obj)) return true;
      if (obj is PrimitiveGeometry<D>) {
        var obj0 = (PrimitiveGeometry<D>)obj;
        return Count.Equals(obj0.Count) && PrimitiveCount.Equals(obj0.PrimitiveCount) && Indices.Equals(obj0.Indices);
      }
      return base.Equals(obj);
    }
    public override string ToString() {
      return Count + " " + PrimitiveCount + " " + Indices.ToString();
    }
    public StringBl ToStringBl() {
      return "".Bl() + Count + " " + PrimitiveCount + " " + Indices.Id.ToStringBl();
    }
    
  }

}

namespace Bling.TestP {
  public interface IA {
    DoubleBl P { get; }
    DoubleBl F(DoubleBl ArgA);
  }
  public interface IB : IA {
    DoubleBl Q { get; }
  }
  public interface IC : IA {
    DoubleBl R { get; }
  }
  public interface A : IBaseObject<IA, A>, IA { }
  public interface B : IBaseObject<IB, B>, IB { }
  public interface C : IBaseObject<IC, C>, IC { }

  public abstract class A<INTF,IMPL> : ActualObject<INTF, IMPL>, IA where INTF : IA where IMPL : IBaseObject<INTF,IMPL>, INTF {
    public DoubleBl P { get; set; }
    public abstract DoubleBl F(DoubleBl ArgA);
  }
  public abstract class Ac : A<IA, A>, A {
    // can't instatiate, here for convenience.
  }
  public class Bc : A<IB, B>, IB {
    public DoubleBl Q { get; set; }
    public override DoubleBl F(DoubleBl ArgA) {
      return (P + Q) * ArgA;
    }
  }
  public class Cc : A<IC, C>, IC {
    public DoubleBl R { get; set; }
    public override DoubleBl F(DoubleBl ArgA) {
      return (P * R) + ArgA;
    }
  }
}
namespace Bling.TestP {
  using Bling.Objects;


  public interface ILight {
    ColorBl Color { get; }
    Double3Bl DirectionToPosition(Double3Bl Position);
    DoubleBl UseAttenuation(Double3Bl Direction, DoubleBl Distance);
  }
  public interface Light : IBaseObject<ILight, Light>, ILight { }
  public abstract class Light<INTF, IMPL> : ActualObject<INTF, IMPL>, ILight where IMPL : IBaseObject<INTF,IMPL>, INTF where INTF : ILight {
    public ColorBl Color { get; set; }
    public abstract Double3Bl DirectionToPosition(Double3Bl Position);
    public abstract DoubleBl UseAttenuation(Double3Bl Direction, DoubleBl Distance);
  }
  public abstract class LightCl : Light<ILight, Light>, Light { }

  public interface IDirectionalLight : ILight {
    Double3Bl Direction { get;  }
  }
  public interface DirectionalLight : IBaseObject<IDirectionalLight, DirectionalLight>, IDirectionalLight { }

  public class DirectionalLightCl : Light<IDirectionalLight,DirectionalLight>, DirectionalLight {
    public DirectionalLightCl() {
      Direction = new Double3Bl(0, 0, -1);
    }
    public Double3Bl Direction { get; set;  }
    public override Double3Bl DirectionToPosition(Double3Bl Position) {
      return -Direction;
    }
    public override DoubleBl UseAttenuation(Double3Bl Direction, DoubleBl Distance) {
      return 1d;
    }
  }

  public interface IAmbientLight : ILight { }
  public interface AmbientLight : IBaseObject<IAmbientLight, AmbientLight>, IAmbientLight { }
  public class AmbientLightCl : Light<IAmbientLight, AmbientLight>, AmbientLight {
    public override DoubleBl UseAttenuation(Double3Bl Direction, DoubleBl Distance) {
      return 0d;
    }
    public override Double3Bl DirectionToPosition(Double3Bl Position) {
      return 0d;
    }
  }
  public interface IAttenuationSet {
    DoubleBl Constant { get; }
    DoubleBl Linear { get;  }
    DoubleBl Quadratic { get; }
    DoubleBl Apply(DoubleBl Displacement);
    //IAttenuationSet Bind { set; }
  }
  public interface AttenuationSet : IBaseObject<IAttenuationSet, AttenuationSet>, IAttenuationSet { }
  public class AttenuationSetCl : ActualObject<IAttenuationSet, AttenuationSet>, AttenuationSet {
    public DoubleBl Constant { get; set;  }
    public DoubleBl Linear { get; set;  }
    public DoubleBl Quadratic { get; set; }
    public DoubleBl Apply(DoubleBl Displacement) {
      return 1d / (Quadratic * Displacement * Displacement +
                   Linear * Displacement + Constant);
    }

  }
  public interface IPointLight : ILight {
    AttenuationSet Attenuation { get; }
    Double3Bl Position { get; }
    DoubleBl Range { get; }
  }
  public interface PointLight : IBaseObject<IPointLight, PointLight>, IPointLight { }
  public abstract class PointLight<INTF, IMPL> : Light<INTF, IMPL>, IPointLight
    where IMPL : IBaseObject<INTF, IMPL>, INTF
    where INTF : IPointLight {
    public AttenuationSet Attenuation { get; set; }
    public Double3Bl Position { get; set; }
    public DoubleBl Range { get; set; }
    public PointLight() {
      Attenuation = new AttenuationSetCl();
      Position = 0d;
      Range = double.PositiveInfinity;
    }
    public override DoubleBl UseAttenuation(Double3Bl Direction, DoubleBl Distance) {
      return this.Attenuation.Apply(Distance);
    }
    public override Double3Bl DirectionToPosition(Double3Bl Position) {
      return this.Position - Position;
    }
  }
  public class PointLightCl : PointLight<IPointLight, PointLight>, PointLight { }

  public interface ISpotLight : IPointLight {
    Double3Bl Direction { get; }
    DegreeBl InnerConeAngle { get; }
    DegreeBl OuterConeAngle { get; }
  }
  public interface SpotLight : IBaseObject<ISpotLight, SpotLight>, ISpotLight { }
  public class SpotLightCl : PointLight<ISpotLight, SpotLight>, SpotLight {
    public Double3Bl Direction { get; set; }
    public DegreeBl InnerConeAngle { get; set; }
    public DegreeBl OuterConeAngle { get; set; }
    public override DoubleBl UseAttenuation(Double3Bl DirectionToPosition, DoubleBl Distance) {
      var dot1 = -this.Direction.Dot(DirectionToPosition);
      var innerCos = this.InnerConeAngle.Cos;
      var outerCos = this.OuterConeAngle.Cos;
      var att = base.UseAttenuation(DirectionToPosition,Distance) * dot1;
      var spot = (dot1 > innerCos).Condition(att, (dot1 > outerCos).Condition(att * (dot1 - outerCos) / (innerCos - outerCos), 0.0));
      return spot;
    }
    public SpotLightCl() {
      Direction = new Double3Bl(0, 0, -1);
      InnerConeAngle = 180.Degrees();
      OuterConeAngle = 90.Degrees();
    }
  }

 
  // can push a light, push a material, a material will suck in all lights above it.
  public interface IMaterial {
    Func<ColorBl, ColorBl> Apply(ILight Light);
  }
  public interface Material : IBaseObject<IMaterial, Material>, IMaterial { }

  public interface IKnobish {
    RgbBl Knob { get; set; }
    void BindKnob(IKnobish Other);
  }
  public interface IDiffuseMaterial : IMaterial, IKnobish {
    IKnobish Ambient { get; set; }
  }
  public interface ISpecularMaterial : IMaterial, IKnobish {
    DoubleBl Power { get; set; }
  }
  public interface IGlassMaterial : ISpecularMaterial {
    ColorBl GlassColor { get; set; }
    DoubleBl RefractiveIndex { get; set; }
    DoubleBl Thickness { get; set; }
    DoubleBl Transparency { get; set; }
  }
  public interface IDiffractionMaterial : ISpecularMaterial {
    // will look at tangent.
    ColorBl HiliteColor { get; set; }
    DoubleBl Distance { get; set; }
    DoubleBl SHparameter { get; set; }
  }


}

namespace Bling.Objects {
  using Bling.Gen;
  // big problem: static coercions and operator overloading. 
  // solution: define IMPL as class. Problem: yes.

  // define interfaces for everything, can then create interface core value when needed.
  // must define two interfaces (a) ILight and (b) Light.

  public interface IBaseObject : ICoreValue {
    BoolBl Isa<INTF0>();
    IMPL0 DownCast0<INTF0, IMPL0>() where IMPL0 : IBaseObject<INTF0, IMPL0>, INTF0;
    IMPL0 UpCast0<INTF0, IMPL0>() where IMPL0 : IBaseObject<INTF0, IMPL0>, INTF0;
  }
  public interface IBaseObjectT<INTF> : IBaseObject { }
  public interface IBaseObject<IMPL> : IBaseObject, ICoreValue<IMPL> where IMPL : IBaseObject<IMPL> { }
  public interface IBaseObject<INTF, IMPL> : IBaseObject<IMPL>, IBaseObjectT<INTF> where IMPL : IBaseObject<INTF, IMPL>, INTF { }

  public abstract class BaseObjectT<INTF> : IBaseObjectT<INTF> {
    protected static readonly Func<INTF, StringBl> ToStringF;
    protected static readonly Func<INTF, INTF, BoolBl> EqualF; // non-structural.

    protected static readonly System.Func<INTF, INTF, bool> EqualsF;
    protected static readonly System.Func<INTF, int> HashCodeF;

    static BaseObjectT() {
      var self = Expression.Parameter(typeof(INTF), "self");
      var other = Expression.Parameter(typeof(INTF), "other");
      Expression hc = Expression.Constant(0);
      Expression eq = Expression.Constant(true);
      foreach (var p in typeof(INTF).GetAllProperties()) {
        (p.CanRead && !p.CanWrite).Assert();
        (typeof(ICoreValue).IsAssignableFrom(p.PropertyType)).Assert();
        var q = Expression.Property(self, p);
        hc = Expression.Add(hc, Expression.Call(q, "GetHashCode", new Type[0] { }));
        var r = Expression.Property(other, p);
        eq = Expression.And(eq, Expression.Equal(q, r));
      }
      EqualsF = Expression.Lambda<System.Func<INTF, INTF, bool>>(eq, self, other).Compile();
      HashCodeF = Expression.Lambda<System.Func<INTF, int>>(hc, self).Compile();

      ToStringF = (target) => {
        var result = "{".Bl();
        bool IsFirst = true;
        foreach (var p in typeof(INTF).GetAllProperties()) {
          if (!IsFirst) result += ", ";
          result += (p.Name.Bl() + "=" + ((ICoreValue) p.GetGetMethod().Invoke(target, new object[0])).ToStringBl());
          IsFirst = false;
        }
        return result + " }";
      };
    }
    public abstract BoolBl Isa<INTF0>();
    public abstract IMPL0 DownCast0<INTF0,IMPL0>() where IMPL0 : IBaseObject<INTF0,IMPL0>, INTF0;
    public abstract IMPL0 UpCast0<INTF0,IMPL0>() where IMPL0 : IBaseObject<INTF0,IMPL0>, INTF0;
    public abstract StringBl ToStringBl();
    //public static implicit operator INTF(BaseObject<INTF> obj) { return (INTF)obj; }
    public static Type ImplementationType { get; internal set; }
  }
  public static class BaseObject<IMPL> where IMPL : IBaseObject<IMPL> {
    public static Type InterfaceType { get; internal set; }
  }
  public abstract class BaseObject<INTF, IMPL> : BaseObjectT<INTF>, IBaseObject<INTF,IMPL> where IMPL : IBaseObject<INTF,IMPL>, INTF {
    static BaseObject() {
      if (BaseObject<IMPL>.InterfaceType != null) throw new InvalidProgramException("Interface should have exactly one implementation");
      if (BaseObjectT<INTF>.ImplementationType != null) throw new InvalidProgramException("Interface should have exactly one implementation");
      BaseObject<IMPL>.InterfaceType = typeof(INTF);
      BaseObjectT<INTF>.ImplementationType = typeof(IMPL);
      foreach (var p in typeof(INTF).GetAllProperties()) {
        (p.CanRead && !p.CanWrite).Assert();
        (typeof(ICoreValue).IsAssignableFrom(p.PropertyType)).Assert();
      }
    }

    public IMPL Table(IntBl Index, IMPL[] Cases) { return BaseTable<INTF, IMPL>.Make(Index, Cases); }
    public IMPL ChooseA(BoolBl Test, IMPL IfFalse) { return BaseChoose<INTF, IMPL>.Make(Test, this, IfFalse); }
    public abstract BoolBl Equal(IMPL Other);

    public static implicit operator IMPL(BaseObject<INTF, IMPL> obj) { 
      return (IMPL)((object) obj); 
    }
  }
  class ObjectGenerator : Generator2 {
    public static readonly ObjectGenerator Instance = new ObjectGenerator();
    private ObjectGenerator() { }
    public override string Name {
      get { return "BlingObjects"; }
    }
  }
  public abstract class CompoundObject<INTF, IMPL> : BaseObject<INTF, IMPL> where IMPL : IBaseObject<INTF, IMPL>, INTF {
    public abstract BRAND Transform<BRAND>(Func<IMPL, BRAND> F) where BRAND : ICoreValue<BRAND>;

    public BRAND Property<BRAND>(System.Reflection.PropertyInfo p) where BRAND : ICoreValue<BRAND> {
      return Transform<BRAND>(impl => (BRAND)p.GetAccessors()[0].Invoke(impl, new object[0]));
    }
    public BRAND Method<BRAND>(System.Reflection.MethodInfo m, object[] Parameters) where BRAND : ICoreValue<BRAND> {
      return Transform<BRAND>(impl => (BRAND) m.Invoke(impl, Parameters));
    }
    internal protected static void Make0<T>(ObjectGenerator.MyBuilder<T> Builder) where T : CompoundObject<INTF,IMPL> {
      Builder.AddInterfaceImplementation(typeof(IMPL));
      Builder.DefinePassThroughConstructors();
      var pminfo0 = typeof(CompoundObject<INTF, IMPL>).GetMethod("Property", new Type[] { typeof(System.Reflection.PropertyInfo) });
      foreach (var p in typeof(INTF).GetAllProperties()) {
        var pp = p;
        var pminfo = pminfo0.MakeGenericMethod(pp.PropertyType);
        Builder.AddInterfaceProperty(pp, t => {
          return (ICoreValue) pminfo.Invoke(t, new object[] { pp });
        }, null);
      }
      var mminfo0 = typeof(CompoundObject<INTF, IMPL>).GetMethod("Method", new Type[] { typeof(System.Reflection.MethodInfo), typeof(object[]) });
      foreach (var m in typeof(INTF).GetMethods()) {
        var mm = m;
        if (mm.IsSpecialName) continue;
        var mminfo = mminfo0.MakeGenericMethod(mm.ReturnType);
        Builder.AddInterfaceMethod(mm, (t,parameters) => {
          return (ICoreValue) mminfo.Invoke(t, new object[] { mm, parameters });
        });
      }
    }
    public override StringBl ToStringBl() {
      return Transform(impl => impl.ToStringBl());
    }
    public override BoolBl Equal(IMPL Other) {
      return Transform(impl => impl.Equal(Other));
    }
    public override BoolBl Isa<INTF0>() {
      return Transform(impl => impl.Isa<INTF0>());
    }
    public override IMPL0 DownCast0<INTF0, IMPL0>() {
      return Transform(impl => impl.DownCast0<INTF0,IMPL0>());
    }
    public override IMPL0 UpCast0<INTF0, IMPL0>() {
      return Transform(impl => impl.UpCast0<INTF0,IMPL0>());
    }
  }
  public abstract class BaseChoose<INTF, IMPL> : CompoundObject<INTF, IMPL> where IMPL : IBaseObject<INTF, IMPL>, INTF {
    public readonly BoolBl Test;
    public readonly IMPL IfTrue, IfFalse;
    public BaseChoose(BoolBl Test, IMPL IfTrue, IMPL IfFalse) {
      this.Test = Test; this.IfTrue = IfTrue; this.IfFalse = IfFalse;
    }
    private static System.Func<BoolBl, IMPL, IMPL, IMPL> MakeF = null;
    public static IMPL Make(BoolBl Test, IMPL IfTrue, IMPL IfFalse) {
      if (MakeF == null) {
        var Bld = ObjectGenerator.Instance.NewClass<BaseChoose<INTF, IMPL>>();
        Make0(Bld);
        var TypeS = Bld.BuildType();
        var constructor = TypeS.GetConstructor(new Type[] { typeof(BoolBl), typeof(IMPL), typeof(IMPL) });
        (constructor != null).Assert();
        MakeF = (a, b, c) => (IMPL) constructor.Invoke(new object[] { a, b, c });
      }
      return MakeF(Test, IfTrue, IfFalse);
    }

    public override BRAND Transform<BRAND>(Func<IMPL, BRAND> F) {
      return Test.Condition(F(IfTrue), F(IfFalse));
    }
    public override int GetHashCode() {
      return IfTrue.GetHashCode() + IfFalse.GetHashCode() + Test.GetHashCode();
    }
    public override bool Equals(object obj) {
      if (obj is BaseChoose<INTF, IMPL>) {
        var obj0 = (BaseChoose<INTF, IMPL>)obj;
        return IfTrue.Equals(obj0.IfTrue) && IfFalse.Equals(obj0.IfFalse) && Test.Equals(obj0.Test);
      }
      return base.Equals(obj);
    }
    public override string ToString() {
      return "if (" + Test.ToString() + ") " + IfTrue.ToString() + " else " + IfFalse.ToString();
    }
  }
  public abstract class BaseTable<INTF, IMPL> : CompoundObject<INTF, IMPL> where IMPL : IBaseObject<INTF, IMPL>, INTF {
    public readonly IntBl Index;
    public readonly IMPL[] Cases;
    public BaseTable(IntBl Index, IMPL[] Cases)
      : base() {
      this.Cases = Cases; this.Index = Index;
    }
    private static System.Func<IntBl,IMPL[],IMPL> MakeF = null;
    public static IMPL Make(IntBl Index, IMPL[] Cases) {
      if (MakeF == null) {
        var Bld = ObjectGenerator.Instance.NewClass<BaseTable<INTF, IMPL>>();
        Make0(Bld);
        var TypeS = Bld.BuildType();
        var constructor = TypeS.GetConstructor(new Type[] { typeof(IntBl), typeof(IMPL[]) });
        (constructor != null).Assert();
        MakeF = (a, b) => (IMPL)constructor.Invoke(new object[] { a, b });
      }
      return MakeF(Index, Cases);
    }
    public override int GetHashCode() {
      int hc = 0;
      hc += Index.GetHashCode();
      foreach (var v in Cases) hc += v.GetHashCode();
      return hc;
    }
    public override bool Equals(object obj) {
      if (obj is BaseTable<INTF, IMPL>) {
        var obj0 = (BaseTable<INTF, IMPL>)obj;
        if (!Index.Equals(obj0.Index)) return false;
        if (Cases.Length != obj0.Cases.Length) return false;
        for (int i = 0; i < Cases.Length; i++) {
          if (!Cases[i].Equals(obj0.Cases[i])) return false;
        }
        return true;
      }
      return base.Equals(obj);
    }
    public override string ToString() {
      var str = "{";
      var isFirst = true;
      foreach (var v in Cases) {
        if (!isFirst) str = str + ", ";
        str = str + v.ToString();
        isFirst = false;
      }
      str = str + "}[";
      return str + Index.ToString() + "]";
    }
    public override BRAND Transform<BRAND>(Func<IMPL, BRAND> F)  {
      var values = new BRAND[Cases.Length];
      for (int i = 0; i < values.Length; i++) values[i] = F(Cases[i]);
      return values.Table(Index);
    }
  }
  public abstract class ActualObject<INTF, IMPL> : BaseObject<INTF, IMPL> where IMPL : IBaseObject<INTF, IMPL>, INTF {
    public override BoolBl Isa<INTF0>() {
      return typeof(INTF0).IsAssignableFrom(typeof(INTF));
    }
    public override IMPL0 DownCast0<INTF0, IMPL0>() {
      return BaseError<INTF0, IMPL0>.Make();
    }
    public override IMPL0 UpCast0<INTF0, IMPL0>() {
      return BaseUpCast<INTF0, IMPL0, INTF, IMPL>.Make(this);
    }
    public static IMPL UpCast<INTF0, IMPL0>(IBaseObject<INTF0, IMPL0> obj) where IMPL0 : IBaseObject<INTF0, IMPL0>, INTF0 {
      return obj.UpCast0<INTF, IMPL>();
    }
    public static IMPL DownCast<INTF0, IMPL0>(IBaseObject<INTF0, IMPL0> obj) where IMPL0 : IBaseObject<INTF0, IMPL0>, INTF0 {
      return obj.DownCast0<INTF, IMPL>();
    }

    public override StringBl ToStringBl() { return ToStringF(this); }
    public override BoolBl Equal(IMPL Other) {
      if (Other is ActualObject<INTF, IMPL>) return EqualF(this, Other);
      else return Other.Equal(this);
    }
    public override int GetHashCode() { return HashCodeF(this); }
    public override bool Equals(object obj) {
      if (obj is ActualObject<INTF, IMPL>) return EqualsF(this, (ActualObject<INTF, IMPL>)obj);
      return base.Equals(obj);
    }
  }
  public abstract class BaseUpCast<INTF, IMPL, INTF2, IMPL2> : BaseObject<INTF, IMPL>
    where IMPL2 : IBaseObject<INTF2, IMPL2>, INTF2
    where IMPL : IBaseObject<INTF, IMPL>, INTF {
    public readonly ActualObject<INTF2, IMPL2> Original;

    private static System.Func<ActualObject<INTF2,IMPL2>,IMPL> MakeF = null;
    public static IMPL Make(ActualObject<INTF2, IMPL2> Original) {
      if (MakeF == null) {
        var Bld = ObjectGenerator.Instance.NewClass<BaseUpCast<INTF, IMPL,INTF2,IMPL2>>();
        Bld.AddInterfaceImplementation(typeof(IMPL));
        Bld.DefinePassThroughConstructors(); // gives us original
        foreach (var p in typeof(INTF).GetAllProperties()) {
          var pp = p;
          Bld.AddInterfaceProperty(pp, (t) => {
            return (ICoreValue) pp.GetAccessors()[0].Invoke(t.Original, new object[0]);
          }, null);
        }
        foreach (var m in typeof(INTF).GetMethods()) {
          var mm = m;
          Bld.AddInterfaceMethod(mm, (t, parameters) => {
            return (ICoreValue) mm.Invoke(t.Original, parameters);
          });
        }
        var TypeS = Bld.BuildType();
        var make = TypeS.GetConstructor(new Type[] { typeof(ActualObject<INTF2, IMPL2>) });
        (make != null).Assert();
        MakeF = (a) => (IMPL) make.Invoke(new object[] { a });
      }
      return MakeF(Original);
    }

    public BaseUpCast(ActualObject<INTF2, IMPL2> Original) {
      this.Original = Original;
    }
    public override IMPL0 DownCast0<INTF0, IMPL0>() {
      if (typeof(INTF0) == typeof(INTF2)) return (IMPL0)(object)Original;
      else if (typeof(INTF0).IsAssignableFrom(typeof(INTF2))) return Original.UpCast0<INTF0, IMPL0>();
      else return Original.DownCast0<INTF0, IMPL0>();
    }
    public override IMPL0 UpCast0<INTF0, IMPL0>() {
      return Original.UpCast0<INTF0, IMPL0>();
    }
    public override BoolBl Isa<INTF0>() {
      return Original.Isa<INTF0>();
    }
    public override BoolBl Equal(IMPL Other) {
      return Original.Equal(Other.DownCast0<INTF2,IMPL2>());
    }
    public override StringBl ToStringBl() { return Original.ToStringBl(); }
    public override int GetHashCode() {
      return typeof(INTF).GetHashCode() + Original.GetHashCode();
    }
    public override bool Equals(object obj) {
      return obj is BaseUpCast<INTF, IMPL, INTF2, IMPL2> && 
        ((BaseUpCast<INTF, IMPL, INTF2, IMPL2>)obj).Original.Equals(Original);
    }
    public override string ToString() {
      return "(" + Original.ToString() + " (" + typeof(INTF).Name + "))";
    }
  }
  public abstract class BaseError<INTF, IMPL> : BaseObject<INTF, IMPL> where IMPL : IBaseObject<INTF, IMPL>, INTF {
    private static System.Func<IMPL> MakeF = null;

    public static IMPL Make() {
      if (MakeF == null) {
        var Bld = ObjectGenerator.Instance.NewClass<BaseError<INTF, IMPL>>();
        Bld.AddInterfaceImplementation(typeof(IMPL));
        Bld.DefinePassThroughConstructors(); // gives us original
        foreach (var p in typeof(INTF).GetAllProperties()) {
          var pp = p;
          var DefaultProp = pp.PropertyType.GetProperty("Default");
          (DefaultProp != null).Assert();
          (DefaultProp.PropertyType == pp.PropertyType).Assert();
          var value = (ICoreValue)DefaultProp.GetAccessors()[0].Invoke(null, new object[0]);
          Bld.AddInterfaceProperty(pp, (t) => value, null);
        }
        foreach (var m in typeof(INTF).GetMethods()) {
          var mm = m;
          var DefaultProp = mm.ReturnType.GetProperty("Default");
          (DefaultProp != null).Assert();
          (DefaultProp.PropertyType == mm.ReturnType).Assert();
          var value = (ICoreValue)DefaultProp.GetAccessors()[0].Invoke(null, new object[0]);
          Bld.AddInterfaceMethod(mm, (t, parameters) => value);
        }
        var TypeS = Bld.BuildType();
        var make = TypeS.GetConstructor(new Type[] { });
        (make != null).Assert();
        MakeF = () => (IMPL)make.Invoke(new object[] { });
      }
      return MakeF();
    }

    public override IMPL0 DownCast0<INTF0, IMPL0>() {
      return BaseError<INTF0, IMPL0>.Make();
    }
    public override IMPL0 UpCast0<INTF0, IMPL0>() {
      return BaseError<INTF0, IMPL0>.Make();
    }
    public override BoolBl Equal(IMPL Other) { return false;  }
    public override bool Equals(object obj) { return obj is BaseError<INTF, IMPL>; }
    public override int GetHashCode() { return typeof(INTF).GetHashCode() - 1; }
    public override BoolBl Isa<INTF0>() { return false; }
    public override StringBl ToStringBl() { return "<error>"; }
    public override string ToString() { return "<error>"; }
  }
}

namespace Bling.Objects {
  public interface IMixin { }
  public interface IMixin<BASE> : IMixin where BASE : IMixin<BASE> { }
  public interface IMixin<BASE,FREEZE> : IMixin<BASE> where BASE : IMixin<BASE> where FREEZE : IMixin<BASE,FREEZE> { }
  public interface IMixin<BASE,FREEZE,INTF> : IMixin<BASE,FREEZE>
    where BASE   : IMixin<BASE>
    where FREEZE : IMixin<BASE,FREEZE>
    where INTF : BASE {
    MIXIN Extend<INTF0,MIXIN>()
      where MIXIN : IMixin<BASE,FREEZE,INTF0>, INTF0, FREEZE
      where INTF0 : INTF;
  
  }
  public abstract class BaseMixin<BASE, FREEZE, INTF> : IMixin<BASE, FREEZE, INTF>
    where BASE : IMixin<BASE>
    where FREEZE : IMixin<BASE, FREEZE>
    where INTF : BASE {
    public MIXIN Extend<INTF0, MIXIN>()
      where MIXIN : IMixin<BASE, FREEZE, INTF0>, INTF0, FREEZE
      where INTF0 : INTF {
      throw new NotSupportedException();
    }

  }

  public interface Geometry :                    IMixin {}
  public interface Geometry<K> : Geometry,       IMixin<Geometry<K>> where K : Dim1D<K> {}
  public interface Geometry<K, J> : Geometry<K>, IMixin<Geometry<K>, Geometry<K, J>>
    where K : Dim1D<K>
    where J : Dim1D<J> {
    /*
    IntBl VertexCount { get; set;  }
    IntBl PrimitiveCount { get; set;  }
    IntBl<J> InstanceCounts { get; set;  }
    FlexNumFuncBl<IntBl, IntBl<K>> Indices { get; set; }
     */
  }
  public interface Geometry<K, J, INTF> : Geometry<K, J>, IMixin<Geometry<K>, Geometry<K, J>, INTF>
    where K : Dim1D<K>
    where J : Dim1D<J>
    where INTF : Geometry<K> {
  }
  public abstract class BaseGeometry<K, J, INTF> : BaseMixin<Geometry<K>, Geometry<K, J>, INTF>, Geometry<K, J, INTF>
    where K : Dim1D<K>
    where J : Dim1D<J>
    where INTF : Geometry<K> {


    /*
    public override MIXIN Extend<INTF0, MIXIN>() {
      throw new NotSupportedException();
    }
    MIXIN IMixin<Geometry<K>, Geometry<K,J>, INTF>.Extend<INTF0, MIXIN>() {
      throw new NotSupportedException();
    }*/

  }


  public interface HasTransform<K> : Geometry<K> where K : Dim1D<K> {

  }

  public interface HasNormal<K> : Geometry<K> where K : Dim1D<K> {
    Double4Bl Normal { get; set; }
  }
  public interface Face : HasNormal<D3> {

  }
  public interface Cube<J> : Face, Geometry<D3, J> where J : Dim1D<J> {

  }


  public interface A<K> : Geometry<K> where K : Dim1D<K> {
    void f();
  }
  public interface B<K> : Geometry<K> where K : Dim1D<K> {
    void g();
  }
  public interface C<K> : Geometry<K> where K : Dim1D<K> {
    void h();
  }
  public interface AB<K> : A<K>, B<K> where K : Dim1D<K> { }
  public interface ABC<K> : AB<K>, C<K> where K : Dim1D<K> { }

  public static partial class Gs<K, J>
    where K : Dim1D<K>
    where J : Dim1D<J> {

    public interface A : Geometry<K, J, A<K>>, A<K> {}
    public interface AB : Geometry<K, J, AB<K>>, AB<K> { }
    public interface ABC : Geometry<K, J, ABC<K>>, ABC<K> { }
  }
  public abstract class ABCX : BaseGeometry<D1,D1,A<D1>>, Gs<D1, D1>.A {
    public void f() { }
    public void g() { }
    public void h() { }

  }

  public class Foo {
    void foo(Gs<D1, D1>.A geom) {
      geom.f();
      
      var geom0 = geom.Extend<AB<D1>,Gs<D1,D1>.AB>();
      geom0.g();
      var geom1 = geom.Extend<ABC<D1>,Gs<D1, D1>.ABC>();
      geom1.h();
    }


  }
}